<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<style>

canvas { display: block; border: 1px solid black; }


</style>
</head>
<body>

<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
<div>fps: <span id="fps"></span></div>
<div>count: <input type="number" id="count" min="0" max="1000000" value="25000"></div>


</body>
<script>

function main() {
  const gl = document.querySelector('canvas').getContext('webgl');
  const ext = gl.getExtension('ANGLE_instanced_arrays');
  if (!ext) {
    return alert('need ANGLE_instanced_arrays');
  }
  twgl.addExtensionsToContext(gl);
  
  const vs = `
  attribute float id;
  attribute vec4 position;
  attribute vec2 texcoord;
  
  uniform float time;
  
  varying vec2 v_texcoord;
  varying vec4 v_color;
  
  void main() {
    float o = id + time;
    gl_Position = position + vec4(
        vec2(
             fract(o * 0.1373),
             fract(o * 0.5127)) * 2.0 - 1.0,
        0, 0);
        
    v_texcoord = texcoord;
    v_color = vec4(fract(vec3(id) * vec3(0.127, 0.373, 0.513)), 1);
  }`;
  
  const fs = `
  precision mediump float;
  varying vec2 v_texcoord;
  varying vec4 v_color;
  
  float circle(in vec2 st, in float radius) {
    vec2 dist = st - vec2(0.5);
    return 1.0 - smoothstep(
       radius - (radius * 0.01),
       radius +(radius * 0.01),
       dot(dist, dist) * 4.0);
  }
  
  void main() {
    gl_FragColor = mix(
       vec4(0), 
       v_color, 
       circle(v_texcoord, 1.0));
  }
  `; 
  
  // compile shaders, link program, look up locations
  const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

  const maxCount = 250000;
  const ids = new Float32Array(maxCount);
  for (let i = 0; i < ids.length; ++i) {
    ids[i] = i;
  }
  const x = 16 / 300 * 2;
  const y = 16 / 150 * 2;
  
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    position: {
      numComponents: 2,
      data: [
       -x, -y,
        x, -y,
       -x,  y,
       -x,  y,
        x, -y,
        x,  y,
     ],
    },
    texcoord: [
        0, 1,
        1, 1,
        0, 0,
        0, 0,
        1, 1,
        1, 0,    
    ],
    id: {
      numComponents: 1,
      data: ids,
      divisor: 1,
    }
  });
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  
  const fpsElem = document.querySelector('#fps');
  const countElem = document.querySelector('#count');
  
  let count;  
  function getCount() {
    count = Math.min(maxCount, parseInt(countElem.value));
  }
  
  countElem.addEventListener('input', getCount);
  getCount();
  
  const maxHistory = 60;
  const fpsHistory = new Array(maxHistory).fill(0);
  let historyNdx = 0;
  let historyTotal = 0;
  
  let then = 0;
  function render(now) {
    const deltaTime = now - then;
    then = now;
    
    historyTotal += deltaTime - fpsHistory[historyNdx];
    fpsHistory[historyNdx] = deltaTime;
    historyNdx = (historyNdx + 1) % maxHistory;
    
    fpsElem.textContent = (1000 / (historyTotal / maxHistory)).toFixed(1);
    
    gl.useProgram(programInfo.program);
    twgl.setUniforms(programInfo, {time: now * 0.001});
    ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, count);
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}
main();


</script>
